iT邦幫忙

2022 iThome 鐵人賽

DAY 26
0
Mobile Development

從開發瀏覽器 APP 學習 Android 實戰技巧,並搭上 Jetpack Compose 的列車系列 第 27

[Day26] 從開發瀏覽器 APP 學習實戰技巧 -- 實作類似 Chrome Extension 的功能

  • 分享至 

  • xImage
  •  

困擾

在瀏覽某些網站時,總會被不斷出現的廣告打擾,或是那些時不時出現在畫面中間要你登入的介面。如果是在 PC 的瀏覽器,使用者一般會安裝 extension 或是 plugin 來隱藏這些資訊。這些 extension 和 plugin 定義了一套機制,可以在特定網頁中注入程式碼,做適度的 html 調整,讓使用者不想看到的元件可以隱藏或刪除。

那麼,如果想在 EinkBro 這麼小的 APP 中,也實作一套類似的機制,要怎麼進行呢?以下將說明目前在 EinkBro 中有針對特定網站做了一些微整型。

實作

建立一個 Web Content Post Processor

建立一個網頁內容處理器。在網頁載入完成後,如果網址屬於某個 domain 時,便注入對應的 Javascript,對 html 內容動手腳。

11行的 postProcess() 會先檢查 WebView 的 url 是不是有符合我們想要調整的網址,如果是的話,就會在 18 行呼叫 ninjaWebView.evaluatejavascript(),帶入 script,調整畫面。

https://ithelp.ithome.com.tw/upload/images/20221006/201402601zfYAu2iXY.png

設定想要處理的網站,和所需要的 Javascript 內容

這邊以 facebook 網站為例:有兩類文章我會想要把它隱藏起來,一是廣告文章;二是推薦的文章。

首先,我們要開啟 PC 版的 Google Chrome,連結到 facebook 網站,並且打開 Developer Tools。在畫面稍微捲動一下,可以看到寫有 Sponsored 的 post,利用 Developer Tools 定位到這則 post,可以看到右邊它是個 article 開頭的 html element。這時,就要利用一點 html 的技巧,從一堆 article element 中的資訊找到足以辨識它就是我們想要隱藏的 post,然後把它寫成 Javascript 撈出來,並且將其隱藏。

https://ithelp.ithome.com.tw/upload/images/20221007/20140260iHcJ0Zlf9p.png

下面就是寫好,針對 mobile 版的 facebook 隱藏廣告 post 的 Javascript 片段。裡頭主要做了兩件事:一是先將目前已經顯示出來的網頁內容,隱藏廣告 post 和推薦 post。然而,大家有用過 facebook mobile 版就會發現,當你滑動畫面幾次後,它會再動態載入更多的內容。所以,必須要在新內容被載入時,再做一次同樣的操作。

對於 Javascript 的實作這裡就不做太多的說明,相信熟悉 Javascript 實作的人,下面的內容都蠻好懂的;對於不熟的人來說,這也不是三言兩語就可以解釋清楚的。建議有興趣的人可以多去看看 Chrome Extensions 的實作,就可以更了解這類的應用。

private const val facebookHideSponsoredPostsJs = """
    javascript:(function() {
    var posts = [].filter.call(document.getElementsByTagName('article'), el => (
          (el.attributes['data-store'] != null && el.attributes['data-store'].value.indexOf('is_sponsored.1') >= 0) || 
          (el.attributes['data-ft'] != null && el.attributes['data-ft'].value.indexOf('is_sponsored') >= 0) || 
          (el.attributes['data-xt-vimp'] != null && el.attributes['data-xt-vimp'].value.indexOf('is_sponsored') >= 0) || 
          (el.getElementsByTagName('header')[0] != null  && el.getElementsByTagName('header')[0].innerText == 'Suggested for you')));
      while(posts.length > 0) { posts.pop().style.display = "none"; }
      
      var ads = Array.from(document.getElementsByClassName("bg-s3")).filter(e => e.innerText.indexOf("Sponsored") != -1);
      ads.forEach(el => {el.style.display="none"; el.nextSibling.style.display="none";el.nextSibling.nextSibling.style.display="none"});
      ads.forEach(el => {el.nextSibling.nextSibling.nextSibling.style.display="none"});
      ads.forEach(el => {el.nextSibling.nextSibling.nextSibling.nextSibling.style.display="none"});
      
    var qcleanObserver = new window.MutationObserver(function(mutation, observer){ 
    var posts = [].filter.call(document.getElementsByTagName('article'), el => (
          (el.attributes['data-store'] != null && el.attributes['data-store'].value.indexOf('is_sponsored.1') >= 0) || 
          (el.attributes['data-ft'] != null && el.attributes['data-ft'].value.indexOf('is_sponsored') >= 0) || 
          (el.attributes['data-xt-vimp'] != null && el.attributes['data-xt-vimp'].value.indexOf('is_sponsored') >= 0) || 
          (el.getElementsByTagName('header')[0] != null  && el.getElementsByTagName('header')[0].innerText == 'Suggested for you')));
      while(posts.length > 0) { posts.pop().style.display = "none"; }
      
      var ads = Array.from(document.getElementsByClassName("bg-s3")).filter(e => e.innerText.indexOf("Sponsored") != -1);
      ads.forEach(el => {el.style.display="none"; el.nextSibling.style.display="none";el.nextSibling.nextSibling.style.display="none"});
      ads.forEach(el => {el.nextSibling.nextSibling.nextSibling.style.display="none"});
      ads.forEach(el => {el.nextSibling.nextSibling.nextSibling.nextSibling.style.display="none"});
    });
    
    qcleanObserver.observe(document, { subtree: true, childList: true });
    })()
"""

在網頁載入後,呼叫 WebContentPostProcessor

網頁內容的後處理,要在網頁載入後才能進行,所以這件事必須在 NinjaWebViewClient::onPageFinished() 中進行。這麼一來,如果一切處理得當,就可以得到眼不見為淨的瀏覽體驗。

https://ithelp.ithome.com.tw/upload/images/20221006/201402603pzX81NsWe.png

缺點

目前的作法很沒有彈性,一旦該網站的介面做了些許的調整,可能這作法就失效了,要再找到新的方式操作。而且現在是寫死在 EinkBro APP 程式碼中。未來可以改進的方向是:建立管理 extension 的介面,讓使用者可以自行建立想要調整的 domain 或 url,再自行填入可行的 javascript 程式碼。

相關連結

WebContentPostProcessor.kt


上一篇
[Day25] 從開發瀏覽器 APP 學習 Android 實戰技巧 -- 開發屬於自己的 Android Studio Plugin
下一篇
[Day27] 從開發瀏覽器 APP 學習實戰技巧 -- Copilot 初體驗,跟 AI pair programming
系列文
從開發瀏覽器 APP 學習 Android 實戰技巧,並搭上 Jetpack Compose 的列車31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言